﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows;
using System.Windows.Media;
using UBConDemo.Models;
using System.Diagnostics;

namespace UBConDemo.CustomControls
{
    public partial class WrapPanel:Panel
    {
        //------------------------------------------------------
        //
        //  Constructors
        //
        //------------------------------------------------------
        #region Constructors

        /// <summary>
        /// Initializes a new instance of UBConDemo.CustomControls.WrapPanel class.
        /// </summary>
        public WrapPanel() 
        {
            ChildSize = new Size(270.0D, 45.0D);
            _transform = new TranslateTransform();
            RenderTransform = _transform;
        }
        #endregion

        //------------------------------------------------------
        //
        //  Properties
        //
        //------------------------------------------------------
        #region Properties

        /// <summary>
        /// Gets or sets child size of the panel.
        /// </summary>
        public Size ChildSize
        {
            get { return (Size)GetValue(ChildSizeProperty); }
            set { SetValue(ChildSizeProperty, value); }
        }

        /// <summary>
        /// Identifies UBConDemo.CustomControls.WrapPanel.ChildSize dependency property.
        /// </summary>
        public static readonly DependencyProperty ChildSizeProperty =
            DependencyProperty.Register("ChildSize", typeof(Size), typeof(WrapPanel));

        /// <summary>
        /// Gets or sets horizontal offset.
        /// </summary>
        public double HorizontalOffset
        {
            get { return (double)GetValue(HorizontalOffsetProperty); }
            set { SetValue(HorizontalOffsetProperty, value); }
        }

        /// <summary>
        /// Identifies UBConDemo.CustomControls.WrapPanel.HorizontalOffset dependency property.
        /// </summary>
        public static readonly DependencyProperty HorizontalOffsetProperty =
            DependencyProperty.Register("HorizontalOffset", typeof(double), typeof(WrapPanel),
            new FrameworkPropertyMetadata(0.0D, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                OnHorizontalOffsetChanged));

        /// <summary>
        /// Gets or sets overall width of panel.
        /// </summary>
        public double ExtentWidth
        {
            get { return (double)GetValue(ExtentWidthProperty); }
            set { SetValue(ExtentWidthProperty, value); }
        }

        /// <summary>
        /// Identifies UBConDemo.CustomControls.WrapPanel.ExtentWidth dependency property.
        /// </summary>
        public static readonly DependencyProperty ExtentWidthProperty =
            DependencyProperty.Register("ExtentWidth", typeof(double), typeof(WrapPanel), 
            new FrameworkPropertyMetadata(0.0D, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        /// <summary>
        /// Gets or sets viewport's width.
        /// </summary>
        public double ViewportWidth
        {
            get { return (double)GetValue(ViewportWidthProperty); }
            set { SetValue(ViewportWidthProperty, value); }
        }

        /// <summary>
        /// Identifies UBConDemo.CustomControls.WrapPanel.ViewportWidth dependency property.
        /// </summary>
        public static readonly DependencyProperty ViewportWidthProperty =
            DependencyProperty.Register("ViewportWidth", typeof(double), typeof(WrapPanel), 
            new FrameworkPropertyMetadata(0.0D, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
        
        /// <summary>
        /// Gets or sets the number of children in UBConDemo.CustomControls.WrapPanel, by which items are splitted.
        /// </summary>
        public int SplitCount
        {
            get { return (int)GetValue(SplitCountProperty); }
            set { SetValue(SplitCountProperty, value); }
        }

        /// <summary>
        /// Identifies UBConDemo.CustomControls.WrapPanel.SplitCount dependency property.
        /// </summary>
        public static readonly DependencyProperty SplitCountProperty =
            DependencyProperty.Register("SplitCount", typeof(int), typeof(WrapPanel), 
            new FrameworkPropertyMetadata(10, 
                FrameworkPropertyMetadataOptions.AffectsMeasure| FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        #endregion

        //------------------------------------------------------
        //
        //  Depenency Property Changed Callbacks
        //
        //------------------------------------------------------
        #region Depenency Property Changed Callbacks

        /// <summary>
        /// Invoked when HorizontalOffset property changes.
        /// </summary>
        /// <param name="sender">Type: System.Windows.DependencyObject. The DependencyObject on which the property has changed value.</param>
        /// <param name="e">Type: System.Windows.DependencyPropertyChangedEventArgs. Event data that is issued by any event that tracks changes to the effective value of this property.</param>
        private static void OnHorizontalOffsetChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            WrapPanel panel = sender as WrapPanel;
            double newValue=(double)e.NewValue;
            panel._transform.X = (-1) * newValue;
        }

        #endregion

        //------------------------------------------------------
        //
        //  Overrides
        //
        //------------------------------------------------------
        #region Overrides

        /// <summary>
        /// Invalidates the measurement state (layout) for the element. (Inherited from UIElement.)
        /// </summary>
        /// <param name="availableSize">The available size that this element can give to child elements. 
        /// Infinity can be specified as a value to indicate that the element will size to whatever content is available.</param>
        /// <returns>The size that this element determines it needs during layout, based on its calculations of child element sizes.</returns>
        protected override Size MeasureOverride(Size availableSize)
        {
            if (availableSize.Width == double.PositiveInfinity || availableSize.Height == double.PositiveInfinity)
                return Size.Empty;

            UpdateScrollInfo(availableSize);
            int count = InternalChildren.Count;
            UIElement uiElement = null;
            for (int i = 0; i < count; i++)
            {
                uiElement = InternalChildren[i];
                uiElement.Measure(ChildSize);
            }

            return availableSize;
        }

        /// <summary>
        /// Invalidates the arrange state (layout) for the element. 
        /// After the invalidation, the element will have its layout updated, which will occur 
        /// asynchronously unless subsequently forced by UpdateLayout. (Inherited from UIElement.)
        /// </summary>
        /// <param name="finalSize">The finalSize size that this element can give to child elements. 
        /// Infinity can be specified as a value to indicate that the element will size to whatever content is available.</param>
        /// <returns>The size that this element determines it needs during layout, based on its calculations of child element sizes.</returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            UpdateScrollInfo(finalSize);
            double x = 0, y = 0;
            int count = InternalChildren.Count, counter = 0;
            UbConItemModel model = null;
            UIElement uiElement = null;
            Rect _measureRect = new Rect()
            {
                Height = ChildSize.Height,
                Width = ChildSize.Width
            };

            for (int i = 0; i < count; i++)
            {
                uiElement = InternalChildren[i];
                model = uiElement.GetValue(FrameworkElement.DataContextProperty) as UbConItemModel;
                if (model == null)
                {
                    throw new NullReferenceException("Model cannot be null. Value: model");
                }

                if (counter > SplitCount - 1)
                {
                    y = 0;
                    x += ChildSize.Width;
                    counter = 0;
                    if (!model.IsCategoryNameItem)
                    {
                        y = ChildSize.Height;
                        counter = 1;
                    }
                }

                if (counter==SplitCount-1)
                {
                    if (model.IsCategoryNameItem)
                    {
                        x += ChildSize.Width;
                        y = 0;
                        _measureRect.X = x;
                        _measureRect.Y = 0;
                        uiElement.Arrange(_measureRect);
                        counter = 1;
                    }
                    else
                    {
                        _measureRect.X = x;
                        _measureRect.Y = y;

                        uiElement.Arrange(_measureRect);
                        counter++;
                    }
                }
                else
                {
                    _measureRect.X = x;
                    _measureRect.Y = y;

                    uiElement.Arrange(_measureRect);
                    counter++;
                }

                y += ChildSize.Height;  
            }

            double height = SplitCount * ChildSize.Height;

            if (finalSize.Width > x)
            {
                x = finalSize.Width;
            }
            if (finalSize.Height > height)
            {
                height = finalSize.Height;
            }
            
            return finalSize;
        }
        #endregion

        //------------------------------------------------------
        //
        //  Public Methods
        //
        //------------------------------------------------------
        #region Public Methods
        #endregion

        //------------------------------------------------------
        //
        //  Internal Methods
        //
        //------------------------------------------------------
        #region Internal Methods
        #endregion

        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
        #region Private Methods

        /// <summary>
        /// Updates extent and viewport with specified sizes.
        /// </summary>
        private void UpdateScrollInfo(Size avaliableSize) 
        {
            
            if (ViewportWidth != avaliableSize.Width)
            {
                ViewportWidth = avaliableSize.Width;
            }
            double extent = GetExtent()-ViewportWidth;
            
            if (ExtentWidth != extent) 
            {
                ExtentWidth = extent;
            }

           
        }

        private double GetExtent() 
        {
            double x = 0, y = 0;
            int count = InternalChildren.Count, counter = 0;
            UbConItemModel model = null;
            UIElement uiElement = null;
            
            for (int i = 0; i < count; i++)
            {
                uiElement = InternalChildren[i];
                model = uiElement.GetValue(FrameworkElement.DataContextProperty) as UbConItemModel;
                if (model == null)
                {
                    throw new NullReferenceException("Model cannot be null. Value: model");
                }

                if (counter > SplitCount - 1)
                {
                    y = 0;
                    x += ChildSize.Width;
                    counter = 0;
                    if (!model.IsCategoryNameItem)
                    {
                        y = ChildSize.Height;
                        counter = 1;
                    }
                }


                if (counter == SplitCount - 1)
                {
                    if (model.IsCategoryNameItem)
                    {
                        x += ChildSize.Width;
                        y = 0;
                        counter = 1;
                    }
                    else
                    {
                        counter++;
                    }
                }
                else
                {
                    counter++;
                }
                
                y += ChildSize.Height;
            }

            return x + ChildSize.Width;
        }

        #endregion

        //------------------------------------------------------
        //
        //  Event Handlers
        //
        //------------------------------------------------------
        #region Event Handlers
        #endregion

        //------------------------------------------------------
        //
        //  Fields
        //
        //------------------------------------------------------
        #region Fields
        
        private TranslateTransform _transform;
        
        #endregion

        //------------------------------------------------------
        //
        //  Events
        //
        //------------------------------------------------------
        #region Events
        #endregion

        //------------------------------------------------------
        //
        //  Commands
        //
        //------------------------------------------------------
        #region Commands
        #endregion

      
    }
}
